﻿using AutoMapper;
using KWT.DRG_DIP.DB;
using KWT.DRG_DIP.DO.qcRules;
using KWT.DRG_DIP.DTO.qcRules;
using KWT.DRG_DIP.DTO.Settlement.List;
using KWT.DRG_DIP.ISvc;
using KWT.DRG_DIP.PO.qcRules;
using Microsoft.EntityFrameworkCore.Query;
using Newtonsoft.Json;
using Org.BouncyCastle.Asn1.X509;
using RulesEngine;
using RulesEngine.Models;
using System;
using System.Data;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;
using Rule = RulesEngine.Models.Rule;

namespace KWT.DRG_DIP.Svc.qcRules
{
    /// <summary>
    /// 
    /// </summary>
    public class UserRulesEngineSvc: IUserRulesEngineSvc
    {
        public EFContext ef { get; set; }
        public IMapper _mapper { get; set; }

        #region 将公式表达式转换成 Rule 表达式
        /// <summary>
        /// 将公式表达式转换成 Rule 表达式
        /// </summary>
        /// <param name="tenantID">租户ID</param>
        /// <param name="tenantID">QRID</param>
        /// <param name="RuleName">规则名称</param>
        /// <param name="Expression">表达式</param>
        /// <param name="SuccessEvent">结果</param>
        public Rule TransformExpression(string tenantID, string QRID, string RuleName, string Expression, string SuccessEvent)
        {
            Expression = Trans(Expression);

            Rule rule = new Rule();
            rule.RuleName = RuleName;
            rule.Expression = Expression;
            rule.SuccessEvent = SuccessEvent;

            CheckRule(tenantID, QRID, rule);

            return rule;
        }
        #endregion

        #region 执行规则
        /// <summary>
        /// 执行规则
        /// </summary>
        /// <param name="tenantID">租户ID</param>
        /// <param name="input">规则引擎 入参</param>
        public List<OutRulesResult> ExecRules(string tenantID, ruleInput input)
        {
            #region 加载 已启用的规则库
            var workflows = new List<Workflow>();
            List<Rule> rules = new List<Rule>();

            Workflow exampleWorkflow = new Workflow();
            exampleWorkflow.WorkflowName = "Test";
            exampleWorkflow.Rules = rules;
            workflows.Add(exampleWorkflow);

            var list = ef.Set<qc_Rules>().Where(x => x.isEnable == 1).ToList();
            if(list.Count==0)
            {
                throw new Exception("请启用至少一个规则");
            }
            foreach (var item in list)
            {
                if(!string.IsNullOrEmpty(item.ruleJson))
                {
                    var rule = JsonConvert.DeserializeObject<Rule>(item.ruleJson);
                    if (rule != null)
                    {
                        rules.Add(rule);
                    }
                }
            }
            #endregion

            #region 构造规则引擎
            var bre = new RulesEngine.RulesEngine(workflows.ToArray());
            #endregion

            #region 构造规则参数
            List<RuleParameter> ruleParameters = new List<RuleParameter>();
            var lstValueRange = GetValueRange(tenantID);
            foreach (var item in lstValueRange)
            {
                ruleParameters.Add(new RuleParameter(item.name, item.LstValueRangeInfo.Select(x => x.Code).ToList()));
            }
            ruleParameters.Add(new RuleParameter("ss", input));
            #endregion

            #region 调用指定的 Workflow，并传递参数，获取每个 Rule 的处理结果
            // 调用指定的 Workflow，并传递参数，获取每个 Rule 的处理结果
            List<RuleResultTree> resultList = bre.ExecuteAllRulesAsync("Test", ruleParameters.ToArray()).Result;

            List<OutRulesResult> LstOutRules = new List<OutRulesResult>();
                
            foreach (var item in resultList)
            {
                OutRulesResult result = new OutRulesResult()
                {
                    ruleName = item.Rule.RuleName,
                     result= item.IsSuccess,
                };
                
                if(item.IsSuccess)
                {
                    result.Msg = item.Rule.SuccessEvent;
                }
                else
                {
                    if (!string.IsNullOrEmpty(item.ExceptionMessage))
                    {
                        result.Msg = item.ExceptionMessage;
                    }
                }

                LstOutRules.Add(result);
            }

            return LstOutRules;
            #endregion

        }
        #endregion

        #region 私有方法

        #region 将公式表达式转换成 Rule 表达式
        /// <summary>
        /// 将公式表达式转换成 Rule 表达式
        /// </summary>
        /// <param name="Expression"></param>
        /// <returns></returns>
        private string Trans(string Expression)
        {

            #region 替换基本符号
            Expression = Expression.Replace("大于等于", ">=").Replace("小于等于", "<=").Replace("等于", "==").Replace("大于", ">").Replace("小于", "<");

            Expression = Expression.Replace("为空", "==null").Replace("不为空", "!=null");
            Expression = Expression.Replace("或者", "||").Replace("并且", "&&").Replace("非", "!").Replace("Sqrt", "Math.Sqrt");
            Expression = Expression.Replace("【", "(") .Replace("】", ")");
            #endregion

            var lstFileds = ef.Set<qc_Rules_Fields>().ToList();

            //收费计数
            Expression= TransCostCount(Expression);

            //包含 or 不包含
            Expression = TransContains(Expression);

            //IN or NOTIN
            Expression = TransIn(Expression);

            #region 替换 质控规则--字段表

            foreach (var item in lstFileds)
            {
                Expression = Expression.Replace(item.FildName, "ss." + item.FildNameInnert);
            }
            #endregion

            return Expression;
        }

        #region 包含 or 不包含
        /// <summary>
        /// 包含
        /// </summary>
        /// <param name="Expression"></param>
        public string TransContains(string Expression)
        {
            #region  包含
            MatchCollection matches = Regex.Matches(Expression, @"\S+\s包含\s\S+");
            foreach (var Match in matches)
            {
                string bh = Match.ToString() ?? "";
                if (string.IsNullOrEmpty(bh))
                    continue;

                string[] Fileds = bh.Split(" ");
                if (Fileds.Count() == 3)
                {
                    string repalce = Fileds[0] + ".Contains(" + Fileds[2] + ") ";
                    bool bList = FilesIsList(typeof(ruleInput), Fileds[2].Trim());
                    if (bList || ef.Set<qc_Rules_ValueRange>().Any(x => x.name == Fileds[2]))
                    {
                        repalce = Fileds[0] + ".Any(x=>" + Fileds[2] + ".Contains(x)) ";
                    }

                    Expression = Expression.Replace(bh, repalce);
                }
            }
            #endregion

            #region 不包含
            matches = Regex.Matches(Expression, @"\S+\s不包含\s\S+");
            foreach (var Match in matches)
            {
                string bh = Match.ToString() ?? "";
                if (string.IsNullOrEmpty(bh))
                    continue;

                string[] Fileds = bh.Split(" ");
                if (Fileds.Count() == 3)
                {
                    string repalce = "!" + Fileds[0] + ".Contains(" + Fileds[2] + ") ";
                    bool bList = FilesIsList(typeof(ruleInput), Fileds[2].Trim());
                    if (bList || ef.Set<qc_Rules_ValueRange>().Any(x => x.name == Fileds[2]))
                    {
                        repalce = "!" + Fileds[0] + ".Any(x=>" + Fileds[2] + ".Contains(x)) ";
                    }

                    Expression = Expression.Replace(bh, repalce);
                }
            }
            #endregion

            return Expression;
        }
        #endregion

        #region IN or NOTIN
        /// <summary>
        /// IN or NOTIN
        /// </summary>
        /// <param name="Expression"></param>
        public string TransIn(string Expression)
        {
            #region IN
            MatchCollection matches = Regex.Matches(Expression, @"\S+\sIN\s\((.*?)\)");
            foreach (var Match in matches)
            {
                string bh = Match.ToString() ?? "";
                if (string.IsNullOrEmpty(bh))
                    continue;

                string[] Fileds = bh.Split("IN");
                if (Fileds.Count() == 2)
                {
                    bool bList = FilesIsList(typeof(ruleInput), Fileds[0].Trim());
                    if (bList || ef.Set<qc_Rules_ValueRange>().Any(x => x.name == Fileds[0].Trim()))
                    {
                        string repalce = "(";
                        string[] inFields = Fileds[1].Replace("(", "").Replace(")", "").Split(" or ");
                        foreach (var item in inFields)
                        {
                            repalce += Fileds[0].Trim() + ".Contains(" + item + ") || ";
                        }
                        repalce = repalce.Substring(0, repalce.Length - 3);
                        repalce += " )";
                        Expression = Expression.Replace(bh, repalce);
                    }
                }
            }
            #endregion

            #region NOTIN
            matches = Regex.Matches(Expression, @"\S+\sNOTIN\s\((.*?)\)");
            foreach (var Match in matches)
            {
                string bh = Match.ToString() ?? "";
                if (string.IsNullOrEmpty(bh))
                    continue;

                string[] Fileds = bh.Split("NOTIN");
                if (Fileds.Count() == 2)
                {
                    bool bList = FilesIsList(typeof(ruleInput), Fileds[0].Trim());
                    if (bList || ef.Set<qc_Rules_ValueRange>().Any(x => x.name == Fileds[0].Trim()))
                    {
                        string repalce = "(";
                        string[] inFields = Fileds[1].Replace("(", "").Replace(")", "").Split(" or ");
                        foreach (var item in inFields)
                        {
                            repalce += "!" + Fileds[0].Trim() + ".Contains(" + item + ") && ";
                        }
                        repalce = repalce.Substring(0, repalce.Length - 3);
                        repalce += " )";
                        Expression = Expression.Replace(bh, repalce);
                    }

                }
            }
            #endregion

            return Expression;
        }
        #endregion

        #region 收费计数
        /// <summary>
        /// 收费计数
        /// </summary>
        /// <param name="Expression"></param>
        public string TransCostCount(string Expression)
        {
            MatchCollection matches = Regex.Matches(Expression, @"\(\s*收费计数\s*(.*?\)){2}");
            foreach (var Match in matches)
            {
                string bh = Match.ToString() ?? "";
                if (string.IsNullOrEmpty(bh))
                    continue;
                bh = bh.Substring(bh.IndexOf("(") + 1);
                bh = bh.Substring(0, bh.LastIndexOf(")"));

                string pattern = @"(\(.*?\))"; 
                string[] Fileds = Regex.Split(bh, pattern);
                if (Fileds.Count() == 3)
                {
                    Fileds[1] = Fileds[1].Substring(Fileds[1].IndexOf("(") + 1);
                    Fileds[1] = Fileds[1].Substring(0, Fileds[1].LastIndexOf(")"));

                    pattern = @"\s*(and|or)\s*";
                    var names = Regex.Split(Fileds[1], pattern, RegexOptions.IgnoreCase);
                    string repalce = "(";
                    foreach (var name in names)
                    {
                        if (name.Trim().ToLower() == "and")
                        {
                            repalce += "&&";
                        }
                        else if (name.Trim().ToLower() == "or")
                        {
                            repalce += "||";
                        }
                        else
                        {
                            if(name.Trim().StartsWith("\""))
                            {
                                repalce += "ss.LstCost.Where(x => x.costName ==" + name.Trim();
                            }
                            else
                            {
                                repalce += "ss.LstCost.Where(x => x.costName ==\"" + name.Trim();
                            }
                            if (name.Trim().EndsWith("\""))
                            {
                                repalce += ").Count()" + Fileds[2];
                            }
                            else
                            {
                                repalce += "\").Count()" + Fileds[2];
                            }
                        }
                    }
                    repalce += ")";
                    Expression = Expression.Replace(bh, repalce);
                }
            }

            return Expression;
        }
        #endregion

        #endregion

        #region check rule
        /// <summary>
        /// check rule
        /// </summary>
        /// <param name="tenantID"></param>
        /// <param name="rule"></param>
        private void CheckRule(string tenantID, string QRID, Rule rule)
        {
            #region 构造规则库
            var workflows = new List<Workflow>();
            List<Rule> rules = new List<Rule>();

            Workflow exampleWorkflow = new Workflow();
            exampleWorkflow.WorkflowName = "Test";
            exampleWorkflow.Rules = rules;
            workflows.Add(exampleWorkflow);

            rules.Add(rule);
            #endregion

            #region 构造规则引擎
            var bre = new RulesEngine.RulesEngine(workflows.ToArray());
            #endregion

            #region 构造规则参数
            List<RuleParameter> ruleParameters = new List<RuleParameter>();
            var lstValueRange = GetValueRange(tenantID);
            foreach (var item in lstValueRange)
            {
                ruleParameters.Add(new RuleParameter(item.name, item.LstValueRangeInfo.Select(x=>x.Code).ToList()));
            }

            ruleInput input = new ruleInput();
            ruleParameters.Add(new RuleParameter("ss", input));
            #endregion

            #region 调用指定的 Workflow，并传递参数，获取每个 Rule 的处理结果
            // 调用指定的 Workflow，并传递参数，获取每个 Rule 的处理结果
            List<RuleResultTree> resultList = bre.ExecuteAllRulesAsync("Test", ruleParameters.ToArray()).Result;

            foreach (var item in resultList)
            {

                if (!item.IsSuccess && !string.IsNullOrEmpty(item.ExceptionMessage))
                {
                    throw new Exception(item.ExceptionMessage);
                }
            }
            #endregion

            CheckSame(ruleParameters, rule, QRID);
        }

        /// <summary>
        /// 校验是否重复
        /// </summary>
        /// <param name="ruleParameters"></param>
        /// <param name="rule"></param>
        /// <param name="QRID"></param>
        /// <exception cref="Exception"></exception>
        private void CheckSame(List<RuleParameter> ruleParameters, Rule rule, string QRID)
        {
            var parameters = new List<ParameterExpression>();
            // 动态注入参数到上下文
            foreach (var param in ruleParameters)
            {
                parameters.Add(Expression.Parameter(param.Value.GetType(), param.Name));
            }

            var expr41 = DynamicExpressionParser.ParseLambda(parameters.ToArray(), typeof(bool), rule.Expression);

            var comparer = new CustomExpressionComparer();

            var list = ef.Set<qc_Rules>().Where(x=>x.QRID!= QRID).ToList();
            foreach (var item in list)
            {
                var expr21 = DynamicExpressionParser.ParseLambda(parameters.ToArray(), typeof(bool), item.ruleExpression);
                bool isStructurallyEqual = ExpressionEqualityComparer.Instance.Equals(expr41, expr21);

                bool isEquivalent = comparer.Equals(expr41, expr21); // 返回 true
                if (isEquivalent)
                {
                    throw new Exception("和规则【" + item.ruleName + "】一致");
                }
            }
        }
        #endregion

        #region 获得所有值域
        /// <summary>
        /// 获得所有值域
        /// </summary>
        /// <param name="tenantID"></param>
        /// <returns></returns>
        public List<InValueRange> GetValueRange(string tenantID)
        {
            //if (RedisHelper.HExists("QCRules-" + tenantID, "ValueRange"))
            //{
            //    return RedisHelper.HGet<List<InValueRange>>("QCRules-" + tenantID, "ValueRange");
            //}
            var list = ef.Set<qc_Rules_ValueRange>().Select(x => 
                new InValueRange() { 
                    VRID = x.VRID, 
                    name = x.name, 
                    remark = x.remark, 
                    LstValueRangeInfo = _mapper.Map<List<InValueRangeInfo>>(ef.Set<qc_Rules_ValueRangeInfo>().Where(y => y.VRID == x.VRID).ToList())  
                }).ToList();
            //RedisHelper.HSet("QCRules-" + tenantID, "ValueRange", list);
            return list;
        }
        #endregion

        #region 判断属性类型是否是List
        /// <summary>
        /// 判断属性类型是否是List
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name">表达式字段名</param>
        /// <returns></returns>
        private bool FilesIsList(Type type, string name)
        {
            name = ef.Set<qc_Rules_Fields>().FirstOrDefault(x => x.FildName == name)?.FildNameInnert ?? "";
            Type? propertyType = type.GetProperty(name.Trim())?.PropertyType;
            bool bList = false;
            if (typeof(List<>).IsAssignableFrom(propertyType?.GetGenericTypeDefinition()))
            {
                bList = true;
            }

            return bList;
        }
        #endregion

        #endregion

    }
}
